package cn.gotom.svg;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;
import org.xml.sax.Attributes;

import android.annotation.SuppressLint;
import android.graphics.Matrix;
import cn.gotom.svg.xml.StyleAttribute;
import cn.gotom.svg.xml.XMLParseUtil;

/**
 * @author 裴绍国
 * @author <a href="mailto:peixere@gmail.com">裴绍国</a>
 */
public class SVGElement implements Serializable
{
	public static final long serialVersionUID = 0;
	protected final Logger log = Logger.getLogger(this.getClass());
	public static final String SVG_NS = "http://www.w3.org/2000/svg";

	protected SVGDiagram diagram;

	protected SVGElement parent = null;

	protected final ArrayList<SVGElement> children = new ArrayList<SVGElement>();

	protected String id;

	protected String text;

	protected final HashMap<String, StyleAttribute> attributes = new HashMap<String, StyleAttribute>();

	// private final HashMap<String, StyleAttribute> styleAttributes = new HashMap<String, StyleAttribute>();

	public SVGElement()
	{
		this(null, null);
	}

	public SVGElement(String id, SVGElement parent)
	{
		this.id = id;
		this.parent = parent;
	}

	protected void setDiagram(SVGDiagram diagram)
	{
		this.diagram = diagram;
		diagram.putElement(id, this);
		for (Iterator<SVGElement> it = children.iterator(); it.hasNext();)
		{
			SVGElement ele = (SVGElement) it.next();
			ele.setDiagram(diagram);
		}
	}

	public void loaderStartElement(Attributes attrs, SVGElement parent) throws SVGException
	{
		this.parent = parent;
		int numAttrs = attrs.getLength();
		for (int i = 0; i < numAttrs; i++)
		{
			String name = attrs.getQName(i);
			String value = attrs.getValue(i);
			attributes.put(name, new StyleAttribute(name, value));
		}
		StyleAttribute sty = new StyleAttribute();
		sty.setName(StyleAttribute.Id);
		if (this.getAttribute(sty))
		{
			id = sty.getStringValue();
		}
		else
		{
			sty.setStringValue(UUID.randomUUID().toString());
			id = sty.getStringValue();
			this.addAttribute(sty.getName(), sty.getStringValue());
		}
	}

	public void loaderAddChild(SVGElement child)
	{
		children.add(child);
		child.parent = this;
	}

	public void loaderAddText(String text)
	{
		this.text = text;
	}

	public void loaderEndElement() throws SVGException
	{

	}

	protected void build() throws SVGException
	{
		StyleAttribute attrs = new StyleAttribute();
		if (!getAttribute(attrs.setName(StyleAttribute.Id)))
		{
			attrs.setStringValue(UUID.randomUUID().toString());
			String newId = attrs.getStringValue();
			if (!newId.equals(id))
			{
				diagram.removeElement(id);
				id = newId;
				diagram.putElement(this.id, this);
			}
		}
		else
		{
			diagram.removeElement(id);
			id = attrs.getStringValue();
			diagram.putElement(this.id, this);
		}
		if (getAttribute(attrs.setName(StyleAttribute.style)))
		{
			String style = attrs.getStringValue();
			if (style != null)
			{
				XMLParseUtil.parseStyle(style, attributes);
			}
		}
		for (int i = 0; i < children.size(); ++i)
		{
			SVGElement ele = (SVGElement) children.get(i);
			ele.build();
		}
	}

	public SVGElement getParent()
	{
		return parent;
	}

	void setParent(SVGElement parent)
	{
		this.parent = parent;
	}

	public List<SVGElement> getPath(List<SVGElement> retVec)
	{
		if (retVec == null)
			retVec = new ArrayList<SVGElement>();

		if (parent != null)
		{
			parent.getPath(retVec);
		}
		retVec.add(this);

		return retVec;
	}

	public List<SVGElement> getChildren(List<SVGElement> retVec)
	{
		if (retVec == null)
			retVec = new ArrayList<SVGElement>();

		retVec.addAll(children);

		return retVec;
	}

	public SVGElement getChild(String id)
	{
		for (Iterator<SVGElement> it = children.iterator(); it.hasNext();)
		{
			SVGElement ele = (SVGElement) it.next();
			String eleId = ele.getId();
			if (eleId != null && eleId.equals(id))
				return ele;
		}
		return null;
	}

	public int indexOfChild(SVGElement child)
	{
		return children.indexOf(child);
	}

	public void swapChildren(int i, int j) throws SVGException
	{
		if ((children == null) || (i < 0) || (i >= children.size()) || (j < 0) || (j >= children.size()))
		{
			return;
		}

		SVGElement temp = children.get(i);
		children.set(i, children.get(j));
		children.set(j, temp);
		build();
	}

	public void removeAttribute(String name)
	{
		attributes.remove(name);
	}

	public void addAttribute(String name, String value) throws SVGException
	{
		if (hasAttribute(name))
			throw new SVGException("Attribute " + name + " already exists");
		if ("id".equals(name))
		{
			if (diagram != null)
			{
				diagram.removeElement(id);
				diagram.putElement(value, this);
			}
			this.id = value;
		}
		attributes.put(name, new StyleAttribute(name, value));
	}

	public void setAttribute(String name, String value) throws SVGException
	{
		if (hasAttribute(name))
			removeAttribute(name);
		addAttribute(name, value);
	}

	public boolean getPres(StyleAttribute attribute)
	{
		return getAttribute(attribute);
	}

	public StyleAttribute getAttribute(String name)
	{
		return attributes.get(name);
	}

	public boolean getAttribute(StyleAttribute attribute)
	{
		if (hasAttribute(attribute.getName()))
		{
			StyleAttribute styAttr = (StyleAttribute) attributes.get(attribute.getName());
			attribute.setStringValue(styAttr == null ? "" : styAttr.getStringValue());
			return true;
		}
		return false;
	}

	public boolean hasAttribute(String name)
	{
		return this.attributes.containsKey(name);
	}

	public Set<String> getAttributeKeySets()
	{
		return this.attributes.keySet();
	}

	public void removeChild(SVGElement child) throws SVGException
	{
		if (!children.contains(child))
		{
			throw new SVGException("Element does not contain child " + child);
		}
		children.remove(child);
	}

	public String getId()
	{
		return id;
	}

	public int getNumChildren()
	{
		return children.size();
	}

	public SVGElement getChild(int i)
	{
		return (SVGElement) children.get(i);
	}

	public double lerp(double t0, double t1, double alpha)
	{
		return (1 - alpha) * t0 + alpha * t1;
	}

	public boolean updateTime(double curTime) throws SVGException
	{
		return false;
	}

	static protected Matrix parseTransform(String val) throws SVGException
	{
		final Matcher matchExpression = Pattern.compile("\\w+\\([^)]*\\)").matcher("");

		Matrix retXform = new Matrix();

		matchExpression.reset(val);
		while (matchExpression.find())
		{
			retXform.postConcat(parseSingleTransform(matchExpression.group()));
		}

		return retXform;
	}

	@SuppressLint("DefaultLocale")
	static protected Matrix parseSingleTransform(String val) throws SVGException
	{
		final Matcher matchWord = Pattern.compile("[-.\\w]+").matcher("");

		Matrix matrix = new Matrix();

		matchWord.reset(val);
		if (!matchWord.find())
		{
			// Return identity transformation if no data present (eg, empty string)
			return matrix;
		}

		String function = matchWord.group().toLowerCase();

		LinkedList<String> termList = new LinkedList<String>();
		while (matchWord.find())
		{
			termList.add(matchWord.group());
		}

		float[] terms = new float[termList.size()];
		Iterator<String> it = termList.iterator();
		int count = 0;
		while (it.hasNext())
		{
			terms[count++] = XMLParseUtil.parseFloat((String) it.next());
		}

		// Calculate transformation
		if (function.equals("matrix"))
		{
			matrix.setValues(new float[] { terms[0], terms[2], terms[4], terms[1], terms[3], terms[5], 0, 0, 1, });
		}
		else if (function.equals("translate"))
		{
			if (terms.length == 1)
			{
				matrix.preTranslate(terms[0], 0);// postTranslate
			}
			else
			{
				matrix.preTranslate(terms[0], terms[1]);// postTranslate
			}
		}
		else if (function.equals("scale"))
		{
			if (terms.length > 1)
				matrix.postScale(terms[0], terms[1]);
			else
				matrix.postScale(terms[0], terms[0]);
		}
		else if (function.equals("rotate"))
		{
			if (terms.length > 0)
			{
				float angle = terms[0];
				float cx = 0;
				float cy = 0;
				if (terms.length > 2)
				{
					cx = terms[1];
					cy = terms[2];
					matrix.preRotate(angle, cx, cy);
				}
				else
				{
					matrix.preRotate(angle);
				}
				// matrix.postTranslate(cx, cy);
				// matrix.postRotate(angle);
				// matrix.postTranslate(-cx, -cy);
			}
			else
			{
				throw new SVGException("Invalid transform list: " + val);
			}
		}
		else if (function.equals("skewx"))
		{
			matrix.postSkew((float) Math.toRadians(terms[0]), 0.0F);
		}
		else if (function.equals("skewy"))
		{
			matrix.postSkew(0.0F, (float) Math.toRadians(terms[0]));
		}
		else
		{
			throw new SVGException("Unknown transform type: " + val);
		}

		return matrix;
	}

	static protected float nextFloat(LinkedList<String> l)
	{
		String s = (String) l.removeFirst();
		return Float.parseFloat(s);
	}

}
